﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using HippoMapper.Entity.Base;
using HippoMapper.DAL.Base;
using System.Reflection;
using Htinns.Common.DynamicCallHelper;
using System.Web;

namespace HippoMapper.MapperService
{
    internal class MapperService : IMapperService
    {
        private static MapperService _instance;

        private static object _syncObject = new object();

        public static MapperService GetInstance()
        {
            if (_instance == null)
            {
                lock (_syncObject)
                {
                    if (_instance == null)
                    {
                        _instance = new MapperService();
                        _instance.Init();

                        System.Threading.Thread.MemoryBarrier();
                    }
                }
            }

            return _instance;
        }

        private IDictionary<string, DynamicCalls<IMapper>.FastCreateInstanceHandler> mapperCreators = new Dictionary<string, DynamicCalls<IMapper>.FastCreateInstanceHandler>();

        private IDictionary<Type, DynamicCalls<IDelayProxy>.FastCreateInstanceHandler> proxyCreators = new Dictionary<Type, DynamicCalls<IDelayProxy>.FastCreateInstanceHandler>();

        private IDictionary<Type, DynamicCalls<IAntiLoopMockEntity>.FastCreateInstanceHandler> mockCreators = new Dictionary<Type, DynamicCalls<IAntiLoopMockEntity>.FastCreateInstanceHandler>();

        private static readonly IDictionary<string, EntityInfoAttribute> entityTableMap =
            new Dictionary<string, EntityInfoAttribute>();

        private static readonly IDictionary<string, PropertyInfoAttribute> propertyColumnMap =
            new Dictionary<string, PropertyInfoAttribute>();

        public static IDictionary<string, EntityInfoAttribute> EntityTableMap
        {
            get { return entityTableMap; }
        }

        public static IDictionary<string, PropertyInfoAttribute> PropertyColumnMap
        {
            get { return propertyColumnMap; }
        }

        /// <summary>
        /// 初始化方法，初始化所有Mapper类，建立与Entity的映射关系
        /// </summary>
        private void Init()
        {
            // - 相对位置
            string rootPath = string.Empty;
            //读取映射表物理位置
            XmlDocument mapperServicePath = new XmlDocument();
            string path = rootPath + @"MapperService\MapperService.xml";
            //mapperServicePath.Load(@"MapperService\MapperService.xml");
            mapperServicePath.Load(path);
            string orMapPath = mapperServicePath.SelectSingleNode("/MapperService").Attributes["map"].Value;

            //加载映射表
            XmlDocument config = new XmlDocument();

            config.Load(rootPath + orMapPath);

            XmlNodeList mapperNodes = config.GetElementsByTagName("mapper");

            //利用反射加载所有的Mapper类对象
            foreach (XmlNode mapperNode in mapperNodes)
            {
                string mapperFullName = mapperNode.Attributes["mapper"].Value;

                string entityFullName = mapperNode.Attributes["entity"].Value;

                string entityAssemblyFullName = mapperNode.Attributes["EntityAssembly"].Value;

                string mapperAssemblyFullName = mapperNode.Attributes["MapperAssembly"].Value;

                Assembly entityAssembly = (String.IsNullOrEmpty(entityAssemblyFullName)) ? Assembly.GetExecutingAssembly() : Assembly.LoadFrom(rootPath + entityAssemblyFullName);
                
                Assembly mapperAssembly = (String.IsNullOrEmpty(mapperAssemblyFullName)) ? Assembly.GetExecutingAssembly() : Assembly.LoadFrom(rootPath + mapperAssemblyFullName);
                
                object rawMapper = mapperAssembly.CreateInstance(mapperFullName, true, BindingFlags.Default, null, null, null, null);

                Type entityType = entityAssembly.GetType(entityFullName);

                if (rawMapper == null)
                {
                    throw new ArgumentException("Error Mapper Init, mapper:" + mapperFullName + ", assembly:" + mapperAssemblyFullName);
                }

                if (rawMapper is IMapper && !(mapperCreators.ContainsKey(entityFullName)))
                {
                    DynamicCalls<IMapper>.FastCreateInstanceHandler createInstanceHandler = DynamicCalls<IMapper>.GetInstanceCreator(mapperAssembly.GetType(mapperFullName));

                    mapperCreators.Add(entityFullName, createInstanceHandler);

                    if (entityType.GetInterface((typeof(IDelayEntity)).FullName) != null && !proxyCreators.ContainsKey(entityType))
                    {
                        Type proxyType = EntityProxyGenerator.GetDelayEntityProxyType(entityType);

                        proxyCreators.Add(entityType, DynamicCalls<IDelayProxy>.GetInstanceCreator(proxyType));
                    }

                    object[] entityInfos = entityType.GetCustomAttributes(typeof(EntityInfoAttribute), false);

                    if (entityInfos != null && entityInfos.Length == 1)
                    {
                        EntityInfoAttribute entityInfo = (EntityInfoAttribute)entityInfos[0];

                        ((IMapper)rawMapper).SetEntityInfo(entityInfo.TableName);

                        EntityTableMap.Add(entityFullName, entityInfo);
                    }

                    PropertyInfo[] properties = entityType.GetProperties(BindingFlags.Instance
                        | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod
                        | BindingFlags.Static | BindingFlags.DeclaredOnly);

                    foreach (PropertyInfo info in properties)
                    {
                        object[] propertyInfos = info.GetCustomAttributes(typeof(PropertyInfoAttribute), false);

                        if (propertyInfos != null && propertyInfos.Length == 1)
                        {
                            PropertyInfoAttribute entityMapperInfo = (PropertyInfoAttribute)propertyInfos[0];

                            if (String.IsNullOrEmpty(entityMapperInfo.PropertyString))
                                entityMapperInfo.PropertyString = info.Name;

                            ((IMapper)rawMapper).AddPropertyInfo(entityMapperInfo.Field, entityMapperInfo);

                            PropertyColumnMap.Add(entityFullName + "." + info.Name, entityMapperInfo);
                        }
                    }

                    //通过特性加载 属性名-加载属性的委托 的映射关系，加载对容器成员的拦截方法
                    IMapper mapper = rawMapper as IMapper;
                    MethodInfo[] infos = mapper.GetType().GetMethods(BindingFlags.Instance 
                        | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod 
                        | BindingFlags.Static | BindingFlags.DeclaredOnly);
                    foreach (MethodInfo method in infos)
                    {

                        foreach (Attribute attr in method.GetCustomAttributes(true))
                        {
                            //这是一个延迟加载属性的方法
                            if (attr is DelayLoadAttribute && method.IsStatic)
                            {
                                IDelayEntityMapper inertMapper = mapper as IDelayEntityMapper;

                                DelayLoadAttribute attribute = attr as DelayLoadAttribute;

                                if (attribute != null)
                                {
                                    inertMapper.RegisterInertLoadDelegate(attribute.FieldName, Delegate.CreateDelegate(inertMapper.SyncFieldHandlerType, null, method));
                                }
                            }

                            //这是一个对实体容器增、插、删、清除操作的拦截方法
                            else if (attr is EntityCollectionInterceptAttribute)
                            {
                                EntityCollectionInterceptAttribute attribute = attr as EntityCollectionInterceptAttribute;

                                if (attribute != null)
                                {
                                    Type collectionType = attribute.CollectionType;

                                    Type interceptHandlerType = collectionType.InvokeMember("GetInterceptHandlerType", BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null) as Type;

                                    try
                                    {
                                        Delegate invokeDelegate = Delegate.CreateDelegate(interceptHandlerType, null, method);
                                        collectionType.GetEvent(attribute.EventName).AddEventHandler(null, invokeDelegate);
                                    }
                                    catch (ArgumentException ae)
                                    {
                                        string methodName = method.ToString();
                                        throw new ArgumentException("Error Init EntityCollection Interceptor At " + mapper.ToString() + "'s Method\"" + methodName + "\"");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        

        #region 批量实体操作方法

        /// <summary>
        /// 批量同步属性方法
        /// </summary>
        /// <param name="entityType">欲同步的惰性实体类的类型</param>
        /// <param name="entities">欲同步的惰性实体类对象集合</param>
        /// <param name="indexs">欲同步的属性名数组</param>
        public void SynchronizeField(Type entityType, IEnumerable<IDelayEntity> entities, string[] indexs)
        {
            TypeCheck(entityType);

            IDelayEntityMapper mapper = mapperCreators[entityType.FullName]() as IDelayEntityMapper;

            if (mapper != null)
            {
                mapper.SynchronizeField(entities, indexs);
            }
        }

        /// <summary>
        /// 批量添加实体类方法
        /// </summary>
        /// <param name="entityType">欲添加的实体类的类型</param>
        /// <param name="entities">欲添加的实体类对象集合</param>
        public void BatchAdd(Type entityType, IEnumerable<IEntity> entities)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.BatchAdd(entities);
            }
        }

        public void BatchAdd(Type entityType, IEnumerable<IEntity> entities, IConditionParameter parameter)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.BatchAdd(entities, parameter);
            }
        }

        /// <summary>
        /// 批量删除实体类对象方法
        /// </summary>
        /// <param name="entityType">欲删除的实体类类型</param>
        /// <param name="entities">欲删除的实体类对象集合</param>
        public void BatchDelete(Type entityType, IEnumerable<IEntity> entities)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.BatchDelete(entities);
            }
        }

        public void BatchDelete(Type entityType, IEnumerable<IEntity> entities, IConditionParameter parameter)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.BatchDelete(entities, parameter);
            }
        }


        /// <summary>
        /// 批量更新实体类对象方法
        /// </summary>
        /// <param name="entityType">欲更新的实体类的类型</param>
        /// <param name="entities">欲更新的实体类对象集合</param>
        public void BatchUpdate(Type entityType, IEnumerable<IEntity> entities)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.BatchUpdate(entities);
            }
        }

        public void BatchUpdate(Type entityType, IEnumerable<IEntity> entities, IConditionParameter parameter)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.BatchUpdate(entities, parameter);
            }
        }

        /// <summary>
        /// 批量获取实体类对象的方法
        /// </summary>
        /// <param name="entityType">欲获取的实体类的类型</param>
        /// <param name="keys">获取实体类所需要的键</param>
        /// <returns></returns>
        public IEnumerable<IEntity> BatchGet(Type entityType, IConditionParameter parameter)
        {
            TypeCheck(entityType);

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                return mapper.BatchGet(parameter);
            }

            return null;
        }

        public IEnumerable<IDelayEntity> BatchGet(Type entityType, IConditionParameter parameter, bool isLazy)
        {
            TypeCheck(entityType);

            IDelayEntityMapper mapper = mapperCreators[entityType.FullName]() as IDelayEntityMapper;

            if (mapper != null)
            {
                return mapper.BatchGet(parameter, isLazy);
            }

            return null;
        }

        #endregion

        #region 单个实体操作方法

        public void SynchronizeField(IDelayEntity entity, string[] indexs)
        {
            TypeCheck(entity.GetType());

            IDelayEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IDelayEntityMapper;

            if (mapper != null)
            {
                mapper.SynchronizeField(entity, indexs);
            }
        }

        public void Add(IEntity entity)
        {
            TypeCheck(entity.GetType());

            IEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.Add(entity);
            }
        }

        public void Add(IEntity entity, IConditionParameter parameter)
        {
            TypeCheck(entity.GetType());

            IEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.Add(entity, parameter);
            }
        }

        public void Delete(IEntity entity)
        {
            TypeCheck(entity.GetType());

            IEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.Delete(entity);
            }
        }

        public void Delete(IEntity entity, IConditionParameter parameter)
        {
            TypeCheck(entity.GetType());

            IEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.Delete(entity, parameter);
            }
        }

        public void Update(IEntity entity)
        {
            TypeCheck(entity.GetType());

            IEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.Update(entity);
            }
        }

        public void Update(IEntity entity, IConditionParameter parameter)
        {
            TypeCheck(entity.GetType());

            IEntityMapper mapper = mapperCreators[entity.GetType().FullName]() as IEntityMapper;

            if (mapper != null)
            {
                mapper.Update(entity, parameter);
            }
        }

        public IEntity Get(Type entityType, IConditionParameter parameter)
        {
            TypeCheck(entityType);

            MapperContextBase context = MapperContextProvider.GetMapperContext();

            if (context.Contains(new KeyValuePair<Type, IConditionParameter>(entityType, parameter)))
            {

            }

            IEntityMapper mapper = mapperCreators[entityType.FullName]() as IEntityMapper;

            if (mapper != null)
            {
                return mapper.Find(parameter);
            }

            return null;
        }

        public IDelayEntity Get(Type entityType, IConditionParameter parameter, bool isLazy)
        {
            TypeCheck(entityType);

            IDelayEntityMapper mapper = mapperCreators[entityType.FullName]() as IDelayEntityMapper;

            if (mapper != null)
            {
                IDelayEntity entity = mapper.Find(parameter, isLazy);

                if (isLazy && proxyCreators.ContainsKey(entityType))
                {
                    IDelayProxy proxy = proxyCreators[entityType]();

                    proxy.SetInstanct(entity);

                    entity = proxy;
                }

                return entity;
            }

            return null;
        }

        #endregion

        private void TypeCheck(Type entityType)
        {
            if (entityType == null || !mapperCreators.ContainsKey(entityType.FullName))
            {
                throw new ArgumentException("Encounter Error Type:" + entityType);
            }
        }


    }
}
